package com.hzgj.bcl.soa.cicada.pool;

import com.hzgj.bcl.soa.cicada.handle.IdleHandler;
import com.hzgj.bcl.soa.cicada.handle.NettyHandler;
import com.hzgj.bcl.soa.cicada.handle.SocketProcessor;
import com.hzgj.bcl.soa.cicada.sender.CicadaSender;
import com.hzgj.bcl.soa.config.ChannelConfig;
import com.hzgj.bcl.soa.rpc.IpAddress;
import com.hzgj.bcl.soa.util.ServiceLoaderUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;

import io.netty.channel.Channel;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.pool.AbstractChannelPoolMap;
import io.netty.channel.pool.ChannelPool;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.Builder;

/**
 * Created by warning5 on 2017/3/3.
 */
public class ChannelPoolFactory {

    static ChannelPoolFactory channelPoolFactory = new ChannelPoolFactory();
    private Logger logger = LoggerFactory.getLogger(getClass());
    private AbstractChannelPoolMap<ChannelPoolBuilder, ChannelPool> channelPoolChannelPoolContainer;

    private ChannelPoolFactory() {
        channelPoolChannelPoolContainer = new AbstractChannelPoolMap<ChannelPoolBuilder, ChannelPool>() {
            @Override
            protected ChannelPool newPool(ChannelPoolBuilder key) {
                return newPool0(key);
            }

        };
    }

    public static ChannelPoolFactory getInstance() {
        return channelPoolFactory;
    }

    public ChannelPool getChannelPool(ChannelPoolBuilder channelPoolBuilder) {
        return channelPoolChannelPoolContainer.get(channelPoolBuilder);
    }

    public boolean removeChannelPool(ChannelPoolBuilder key) {
        return channelPoolChannelPoolContainer.remove(key);
    }

    private ChannelPool newPool0(ChannelPoolBuilder channelPoolBuilder) {
        return new DefaultChannelPool(channelPoolBuilder.channelPoolConfig, channelPoolBuilder.ipAddress, channelPoolBuilder.channelConfig, new DefaultChannelPool.SimpleChannelPoolHandler() {
            @Override
            public void channelCreated(Channel channel) throws Exception {
                if (logger.isTraceEnabled()) {
                    logger.trace("channelCreated");
                }
                ChannelConfig channelConfig = channelPoolBuilder.channelConfig;
                ChannelPipeline pipeline = channel.pipeline();
                IdleStateHandler idleStateHandler = new IdleStateHandler(channelConfig.getIdleTimeSeconds(),
                        channelConfig.getIdleTimeSeconds(), channelConfig.getIdleTimeSeconds());
                pipeline.addFirst("idleStat", idleStateHandler);
                pipeline.addFirst("idleHandler", new IdleHandler());

                pipeline.addLast("decoder", ServiceLoaderUtil.getDecoder(channelConfig.getProtocol()));
                pipeline.addLast("encoder", ServiceLoaderUtil.getEncoder(channelConfig.getProtocol()));
                pipeline.addLast("handler", new NettyHandler(new SocketProcessor(channelPoolBuilder.sender)));
            }
        });
    }

    @Builder
    public static class ChannelPoolBuilder {
        IpAddress ipAddress;
        String poolKey;
        ChannelConfig channelConfig;
        ChannelPoolConfig channelPoolConfig;
        CicadaSender sender;

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            ChannelPoolBuilder that = (ChannelPoolBuilder) o;
            return Objects.equals(ipAddress, that.ipAddress) &&
                    Objects.equals(poolKey, that.poolKey);
        }

        @Override
        public int hashCode() {
            return Objects.hash(ipAddress, poolKey);
        }
    }
}

